(function() {
    Nokia.DatePicker = Nokia.Widget.extend({
        initialize: function(options) {
            var instance = this;
            instance.regional = {};

            var defaults = {
                element: 'body',
                format: 'dd.mm.yy',
                title: 'Pick a date',
                yearRange: 10,
                alwaysVisible: false,
                i18n: '',
                navigate: function() {}
            };

            instance.options = jQuery.extend(defaults, options);

            // loading i18n
            jQuery.extend(instance.options, Nokia.DatePicker.regional[instance.options.i18n]);

            instance._super.apply(instance, [instance.options]);

			instance.registerData("datepicker");

			instance.currentDate = new Date();

            instance._create();

            instance._attatchEvents();

            instance._setDate(instance.currentDate);

            instance.registerVibrationOn(['changeView', 'navigate', 'setDay', 'setMonth', 'setYear', 'setDate']);

            instance.fireCallback('create', null);
        },

        _attatchEvents: function() {
        	var instance = this;
        	var options = instance.options;
        	var Styles = instance.Styles;

        	instance.calendarImage.bind('focus.datepicker', function() {
        		Nokia.util.addClass(this, Styles.imageHover);
        	});

        	instance.calendarImage.bind('blur.datepicker', function() {
        		Nokia.util.removeClass(this, Styles.imageHover);
        	});

        	instance.calendarImage.hover(function() {
        		instance.calendarImage.trigger('focus.datepicker');
        	}, function() {
        		instance.calendarImage.trigger('blur.datepicker');
        	});

        	var imageAction = function() {
        		if (!options.alwaysVisible) {
	        		instance.calendarWrapper.toggleClass(Styles.nokiaHidden);

    	    		instance.fireCallback('changeView');
        		}
        	};

        	instance.calendarImage.bind('click.datepicker', imageAction);
        	Nokia.util.bindEnter(instance.calendarImage, imageAction);

			var leftAction = function() {
        		instance.setMonth(instance.currentDate.getMonth() - 1);

        		instance._updateDayValues();
        	};

        	instance.arrowLeft.bind('click.datepicker', leftAction);
        	Nokia.util.bindEnter(instance.arrowLeft, leftAction);

        	instance.arrowLeft.hover(function() {
        		instance.arrowLeft.addClass(Styles.uiStateHover);
        	}, function() {
        		instance.arrowLeft.removeClass(Styles.uiStateHover);
        	});

        	var rightAction = function() {
        		instance.setMonth(instance.currentDate.getMonth() + 1);

        		instance._updateDayValues();
        	};

        	instance.arrowRight.bind('click.datepicker', rightAction);
        	Nokia.util.bindEnter(instance.arrowRight, rightAction);

        	instance.arrowRight.hover(function() {
        		instance.arrowRight.addClass(Styles.uiStateHover);
        	}, function() {
        		instance.arrowRight.removeClass(Styles.uiStateHover);
        	});

        	instance.currentDateLabel.bind('keyup', function(event) {

        		Nokia.util.addClass(this, Styles.inputError);

        		try {
        			var value = this.value;
        			var formatedDate = instance._formatDate(options.format, new Date());

        			if ( value.length == formatedDate.length ) {

        				var date = instance._decodeDate(value, options.format);

        				if (date && date.getDate()) {

        					instance.setDate(date);

        					Nokia.util.removeClass(this, Styles.inputError);
    					}

    				}

        		} catch (e) {
        			throw e;
        		}

        	});

        },

        _cleanHoverState: function() {
        	var instance = this;
        	var Styles = instance.Styles;

        	jQuery.each(instance.dayValuesElements, function(i, item) {
        		Nokia.util.removeClass( item[0], Styles.uiStateHover );
        	});

        },

        _create: function() {
            var instance = this;
            var options = instance.options;
            var element = instance.element;
            var Styles = instance.Styles;

            instance.wrapper = Nokia.dom.parseHTML('<div class="' + Styles.datepicker +  '"></div>');
			instance.calendarWrapper = Nokia.dom.parseHTML('<div class="' + [Styles.calendarWrapper, Styles.nokiaHidden, Styles.uiWidgetHeader, Styles.uiStateDefault, Styles.uiHelperClearFix].join(' ') + '"></div>');

			if (options.alwaysVisible) {
				Nokia.util.removeClass(instance.calendarWrapper[0], Styles.nokiaHidden);
			}

			instance.navigationWrapper = Nokia.dom.parseHTML('<div class="' + [Styles.navigationWrapper, Styles.uiWidgetHeader, Styles.uiWidgetTitlebar, Styles.uiStateActive].join(' ') + '"></div>');
			instance.dayNamesWrapper = Nokia.dom.parseHTML('<div class="' + [Styles.weeksWrapper, Styles.uiHelperClearFix].join(' ') + '"></div>');
			instance.daysWrapper = Nokia.dom.parseHTML('<div class="' + Styles.daysWrapper + '"></div>');

			instance.arrowLeft = Nokia.dom.parseHTML('<a href="javascript:void(0);" class="' + [Styles.arrow, Styles.arrowLeft].join(' ') + '"></a>');
			instance.arrowRight = Nokia.dom.parseHTML('<a href="javascript:void(0);" class="' + [Styles.arrow, Styles.arrowLeft].join(' ') + '"></a>');

			instance.arrowLeftIcon = Nokia.dom.parseHTML('<div class="' + [Styles.arrowIcon, Styles.arrowIconLeft, Styles.uiIcon, Styles.uiIconTriagleW].join(' ') + '"></div>');
			instance.arrowRightIcon = Nokia.dom.parseHTML('<div class="' + [Styles.arrowIcon, Styles.arrowIconRight, Styles.uiIcon, Styles.uiIconTriagleE].join(' ') + '"></div>');

			instance.monthLabel = Nokia.dom.parseHTML('<label class="' + Styles.labelMonth + '"></label>');

			instance.currentDateLabel = Nokia.dom.parseHTML('<input type="text" class="' + Styles.labelDate + '" />');
			instance.calendarImage = Nokia.dom.parseHTML('<a class="' + Styles.image + '" href="javascript:void(0);"></a>');

			Nokia.util.addClass(element[0], [Styles.element, Styles.uiWidgetHeader, Styles.uiStateDefault, Styles.uiHelperClearFix].join(' '));

			Nokia.dom.wrap(instance.wrapper, element);

			Nokia.dom.append(element, instance.currentDateLabel);
			Nokia.dom.append(element, instance.calendarImage);

			Nokia.dom.append(instance.wrapper, instance.calendarWrapper);

			Nokia.dom.append(instance.calendarWrapper, instance.navigationWrapper);
			Nokia.dom.append(instance.calendarWrapper, instance.dayNamesWrapper);
			Nokia.dom.append(instance.calendarWrapper, instance.daysWrapper);

			Nokia.dom.append(instance.arrowLeft, instance.arrowLeftIcon);
			Nokia.dom.append(instance.arrowRight, instance.arrowRightIcon);

			Nokia.dom.append(instance.navigationWrapper, instance.arrowLeft);
			Nokia.dom.append(instance.navigationWrapper, instance.monthLabel);
			Nokia.dom.append(instance.navigationWrapper, instance.arrowRight);

			instance._populateDayNames();

			instance._populateDayValues();

			instance._updateDayValues();
        },

        _populateDayNames: function() {
        	var instance = this;
        	var Styles = instance.Styles;
        	var dayNamesShort = instance._getDayNamesShort();

        	jQuery.each(dayNamesShort, function(i, dayName) {
        		var dayNameWrapper = Nokia.dom.parseHTML('<label class="' + Styles.weekLabel + '">' + dayName + '</label>');

        		Nokia.dom.append(instance.dayNamesWrapper, dayNameWrapper);
        	});
        },

        _populateDayValues: function(year, month) {
        	var instance = this;
        	var options = instance.options;
        	var Styles = instance.Styles;

    		instance.dayValuesElements = [];
			instance.blankDays = [];
			instance.biggerDays = [];

    		instance.daysWrapper.empty();

    		for (var i = 0; i < 7; i++) {
				var dayWrapper = Nokia.dom.parseHTML('<div class="' + [Styles.day, Styles.dayBlank].join(' ') + '">&nbsp;</div>');

				instance.blankDays.push(dayWrapper);

				Nokia.dom.append(instance.daysWrapper, dayWrapper);
    		}

    		for (var i = 1; i < 32; i++) {

				var dayWrapper = Nokia.dom.parseHTML('<a href="javascript:void(0);" class="' + [Styles.day, Styles.uiStateDefault].join(' ') + '">' + i + '</a>');

				if (i > 28) {
					instance.biggerDays.push(dayWrapper);
				}

				(function(i) {
					var dateAction = function() {
    					var date = new Date(instance.currentDate.getFullYear(), instance.currentDate.getMonth(), i);

    					instance._setDate(date);

    					if (!options.alwaysVisible) {
							instance._hideCalendar();

							instance.fireCallback('changeView');
						}

						// Non-touch focus fix
						instance._cleanHoverState();
						instance.calendarImage.focus();

					};

					dayWrapper.bind('click.datepicker', dateAction);
					Nokia.util.bindEnter(dayWrapper, dateAction);

					dayWrapper.hover(function() {
						Nokia.util.addClass(this, Styles.uiStateHover);
					}, function() {
						Nokia.util.removeClass(this, Styles.uiStateHover);
					});
				})(i);

				instance.dayValuesElements.push(dayWrapper);

    			if (i == instance.currentDate.getDate()) {
    				instance.dayElement = dayWrapper;

    				Nokia.util.addClass(dayWrapper[0], Styles.uiStateActive);
    			}

        		Nokia.dom.append(instance.daysWrapper, dayWrapper);
    		};
        },

        _hideCalendar: function() {
        	var instance = this;

        	Nokia.util.addClass(instance.calendarWrapper[0], instance.Styles.nokiaHidden);
        },

         _showCalendar: function() {
        	var instance = this;

        	Nokia.util.removeClass(instance.calendarWrapper[0], instance.Styles.nokiaHidden);
        },

        _updateDayValues: function() {
        	var instance = this;

        	var firstDay = (new Date(
        		instance.currentDate.getFullYear(),
        		instance.currentDate.getMonth(),
        		1)
    		).getDay();

        	jQuery.each(instance.blankDays, function(i, day) {
        		Nokia.util.addClass(day[0], instance.Styles.nokiaHidden);

        		if (firstDay > i) {
        			Nokia.util.removeClass(day[0], instance.Styles.nokiaHidden);
        		}
        	});

        	jQuery.each(instance.biggerDays, function(i, day) {
        		Nokia.util.addClass(day[0], instance.Styles.nokiaHidden);

        		var daysInMonth = instance._getDaysInMonth(instance.currentDate.getFullYear(), instance.currentDate.getMonth());

        		if (parseInt(day.text(), 10) - 1 < daysInMonth) {
        			Nokia.util.removeClass(day[0], instance.Styles.nokiaHidden);
        		}
        	});

        },

        _getDaysInMonth: function(year, month) {
            return 32 - new Date(year, month, 32).getDate();
        },

        _getDayValues: function(year, month) {
            var instance = this;
            var daysInMonth = 0;
            var days = [];

            if (!month) {
                month = instance.currentDate.getMonth();
            }

            if (!year) {
                year = instance.currentDate.getFullYear();
            }

            daysInMonth = instance._getDaysInMonth(year, month);

            for (var i = 1; i < daysInMonth + 1; i++) {
                days.push(i);
            }

            return days;
        },

        _getMonthValues: function() {
            var instance = this,
            months = [];

            for (var i = 0; i < 12; i++) {
                months.push({
                    value: i,
                    caption: instance.options.monthNamesShort[i]
                });
            }

            return months;
        },

        _getDayNamesShort: function() {
        	var instance = this,
            dayNames = [];

            for (var i = 0; i < 7; i++) {
                dayNames.push(
                    instance.options.dayNamesShort[i]
                );
            }

            return dayNames;
        },

        _getYearValues: function() {
            var instance = this;
            var options = instance.options;
            var currentYear = new Date().getFullYear();
            var years = [];
            var range = options.yearRange;

            for (var i = currentYear - range; i < currentYear + range + 1; i++) {
                years.push({
                    value: i,
                    caption: i
                });
            }

            return years;
        },

        _formatDate: function(format, date) {
            var instance = this;
            var options = instance.options;

            if (!format) {
                return '';
            }

            if (!date) {
                return '';
            }

            var dayNamesShort = (options ? options.dayNamesShort: null);
            var dayNames = (options ? options.dayNames: null);
            var monthNamesShort = (options ? options.monthNamesShort: null);
            var monthNames = (options ? options.monthNames: null);

            // Check whether a format character is doubled
            var lookAhead = function(match) {
                var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
                if (matches) {
                    iFormat++;
                }
                return matches;
            };

            // Format a number, with leading zero if necessary
            var formatNumber = function(match, value, len) {
                var num = '' + value;
                if (lookAhead(match)) {
                    while (num.length < len) {
                        num = '0' + num;
                    }
                }
                return num;
            };
            // Format a name, short or long as requested
            var formatName = function(match, value, shortNames, longNames) {
                return (lookAhead(match) ? longNames[value] : shortNames[value]);
            };
            var output = '';
            var literal = false;
            if (date) {
                for (var iFormat = 0; iFormat < format.length; iFormat++) {
                    if (literal) {
                        if (format.charAt(iFormat) == "'" && !lookAhead("'")) {
                            literal = false;
                        } else {
                            output += format.charAt(iFormat);
                        }
                    } else {
                        switch (format.charAt(iFormat)) {
                        case 'd':
                            output += formatNumber('d', date.getDate(), 2);
                            break;
                        case 'D':
                            output += formatName('D', date.getDay(), dayNamesShort, dayNames);
                            break;
                        case 'o':
                            var doy = date.getDate();
                            for (var m = date.getMonth() - 1; m >= 0; m--)
                            doy += this._getDaysInMonth(date.getFullYear(), m);
                            output += formatNumber('o', doy, 3);
                            break;
                        case 'm':
                            output += formatNumber('m', date.getMonth() + 1, 2);
                            break;
                        case 'M':
                            output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
                            break;
                        case 'y':
                            output += (lookAhead('y') ? date.getFullYear() :
                            (date.getYear() % 100 < 10 ? '0': '') + date.getYear() % 100);
                            break;
                        case '@':
                            output += date.getTime();
                            break;
                        case "'":
                            if (lookAhead("'"))
                            output += "'";
                            else
                            literal = true;
                            break;
                        default:
                            output += format.charAt(iFormat);
                        }
                    }
                }
            }

            return output;
        },

        _decodeDate: function(dateString, format) {
		    var instance = this;

		    if (!format) {
		        return '';
		    }

		    if (!dateString) {
		        return '';
		    }

		    for (var i = 0; i < dateString.length; i++) {
		    	var curChar = format.charAt(i);

		    	switch (curChar) {
		    		case 'd':
		    			var day = (dateString.charAt(i)
		    				+ dateString.charAt(i + 1)
						);

		    			i++;

		    			break;
		    		case ('m'):
		    			var month = (dateString.charAt(i)
		    				+ dateString.charAt(i + 1)
						);

		    			i++;

		    			break;
		    		case 'Y':
		    			var year = (dateString.charAt(i)
		    				+ dateString.charAt(i + 1)
						);

		    			i++;

		    			break;
		    		case 'y':
		    			var year = (dateString.charAt(i)
		    				+ dateString.charAt(i + 1)
		    				+ dateString.charAt(i + 2)
		    				+ dateString.charAt(i + 3)
						);

						var fullyear = (new Date()).getFullYear().toString();

						if (year.length < fullyear.length) {
							year = fullyear.substring(0, 2) + year;
						}

		    			i++;

		    			break;
		    	}
		    }

		    return ( new Date(year, (month - 1), day) ) || (new Date());
		},

        _setDate: function(date) {
            var instance = this;
            var options = instance.options;
            var lastDate = instance.currentDate;

            if (!date) {
                date = new Date();
            }

            instance.currentDate = date;

            instance.daysInMonth = instance._getDaysInMonth(date.getFullYear(), date.getMonth());

            if (instance.dayElement) {
            	Nokia.util.removeClass(instance.dayElement[0], instance.Styles.uiStateActive);
            }

            instance.dayElement = instance.dayValuesElements[date.getDate() - 1];

            Nokia.util.addClass(instance.dayElement[0], instance.Styles.uiStateActive);

			instance.currentDateLabel.val(instance._formatDate(options.format, date));
			instance.monthLabel.html(instance._formatDate('MM yy', date));
        },

        _getValidDate: function(year, month, day) {
            var instance = this;
            var daysInMonth = instance._getDaysInMonth(year, month);
            var lastDate = instance.currentDate;

            if (daysInMonth < lastDate.getDate()) {
                return new Date(year, month, (day || daysInMonth));
            }

            return new Date(year, month, (day || lastDate.getDate()));
        },

        getDate: function() {
            var instance = this;
            var options = instance.options;

            return instance._formatDate(options.format, instance.currentDate);
        },

        getDateObject: function() {
        	var instance = this;

        	return instance.currentDate;
        },

        setDay: function(day) {
            var instance = this;

            instance.currentDate.setDate(day);
            instance.setDate(instance.currentDate);

            instance.fireCallback('setDay', day);
        },

        setMonth: function(month) {
            var instance = this;

            instance.setDate(instance._getValidDate(instance.currentDate.getFullYear(), month));

			instance.fireCallback('setMonth', month);
        },

        setYear: function(year) {
            var instance = this;

            instance.setDate(instance._getValidDate(year, instance.currentDate.getMonth()));

			instance.fireCallback('setYear', year);
        },

        setDate: function(date) {
            var instance = this;

            instance._setDate(date);

			instance.fireCallback('setDate', instance.getDate());
        },

        Styles: {
        	arrow: 'nokia-datepicker-arrow',
        	arrowIcon: 'nokia-datepicker-arrow-icon',
        	arrowIconLeft: 'nokia-datepicker-arrow-icon-left',
        	arrowIconRight: 'nokia-datepicker-arrow-icon-right',
        	arrowLeft: 'nokia-datepicker-arrow-left',
        	arrowRight: 'nokia-datepicker-arrow-right',
        	calendarWrapper: 'nokia-datepicker-calendar-wrapper',
        	datepicker: 'nokia-datepicker',
        	day: 'nokia-datepicker-day',
        	dayBlank: 'nokia-datepicker-day-blank',
        	daysWrapper: 'nokia-datepicker-days-wrapper',
        	element: 'nokia-datepicker-element',
        	labelDate: 'nokia-datepicker-label-date',
        	labelMonth: 'nokia-datepicker-label-month',
        	image: 'nokia-datepicker-image',
        	imageHover: 'nokia-datepicker-image-hover',
        	inputError: 'nokia-datepicker-input-error',
        	navigationWrapper: 'nokia-datepicker-navigation-wrapper',
        	nokiaHidden: 'nokia-hidden',
        	uiIcon: 'ui-icon',
        	uiIconTriagleE: 'ui-icon-triangle-1-e',
        	uiIconTriagleW: 'ui-icon-triangle-1-w',
        	uiHelperClearFix: 'ui-helper-clearfix',
        	uiStateActive: 'ui-state-active',
        	uiStateDefault: 'ui-state-default',
        	uiStateHover: 'ui-state-hover',
        	uiWidgetHeader: 'ui-widget-header',
        	uiWidgetTitlebar: 'ui-widget-titlebar',
        	weekLabel: 'nokia-datepicker-week-label',
        	weeksWrapper: 'nokia-datepicker-weeks-wrapper'
        },

        DAY: 'day',

        MONTH: 'month',

        YEAR: 'year'
    });

    Nokia.DatePicker.regional = {};
    Nokia.extend(Nokia.DatePicker.regional, {
        '': {
            // Default regional settings
            closeText: 'Done',
            // Display text for close link
            prevText: 'Prev',
            // Display text for previous month link
            nextText: 'Next',
            // Display text for next month link
            currentText: 'Today',
            // Display text for current month link
            monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
            'July', 'August', 'September', 'October', 'November', 'December'],
            // Names of months for drop-down and formatting
            monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            // For formatting
            dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
            // For formatting
            dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
            // For formatting
            dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
            // Column headings for days starting at Sunday
            dateFormat: 'mm/dd/yy',
            // See format options on parseDate
            firstDay: 0,
            // The first day of the week, Sun = 0, Mon = 1, ...
            isRTL: false
            // True if right-to-left language, false if left-to-right
        }
    });
})();